/**
 * Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/daikit4gxt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.daikit4gxt.client.ui.editor.advanced;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.daikit.commons.shared.bean.AbstractDkBeanWithId;
import com.daikit.daikit4gxt.client.DkBaseMain;
import com.daikit.daikit4gxt.client.propertyaccess.DkEditDeletePropertyAccess;
import com.daikit.daikit4gxt.client.ui.UIInvalidatable;
import com.daikit.daikit4gxt.client.ui.cell.IconButtonCell;
import com.daikit.daikit4gxt.client.ui.columnconfig.DkDeleteColumnConfig;
import com.daikit.daikit4gxt.client.ui.columnconfig.DkEditColumnConfig;
import com.daikit.daikit4gxt.client.ui.edit.AddButtonSelectEvent;
import com.daikit.daikit4gxt.client.ui.edit.AddButtonSelectEvent.AddButtonSelectHandler;
import com.daikit.daikit4gxt.client.ui.edit.AddButtonSelectEvent.HasAddButtonSelectHandlers;
import com.daikit.daikit4gxt.client.ui.edit.CreateButtonSelectEvent;
import com.daikit.daikit4gxt.client.ui.edit.CreateButtonSelectEvent.CreateButtonSelectHandler;
import com.daikit.daikit4gxt.client.ui.edit.CreateButtonSelectEvent.HasCreateButtonSelectHandlers;
import com.daikit.daikit4gxt.client.ui.edit.DeleteButtonSelectEvent;
import com.daikit.daikit4gxt.client.ui.edit.DeleteButtonSelectEvent.DeleteButtonSelectHandler;
import com.daikit.daikit4gxt.client.ui.edit.DeleteButtonSelectEvent.HasDeleteButtonSelectHandlers;
import com.daikit.daikit4gxt.client.ui.edit.EditButtonSelectEvent;
import com.daikit.daikit4gxt.client.ui.edit.EditButtonSelectEvent.EditButtonSelectHandler;
import com.daikit.daikit4gxt.client.ui.edit.EditButtonSelectEvent.HasEditButtonSelectHandlers;
import com.daikit.daikit4gxt.client.ui.editor.DkEditorFunctionality;
import com.daikit.daikit4gxt.client.utils.miscs.DkUIUtils;
import com.google.gwt.event.shared.HandlerRegistration;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.widget.core.client.ContentPanel;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.tips.QuickTip;
import com.sencha.gxt.widget.core.client.toolbar.FillToolItem;
import com.sencha.gxt.widget.core.client.toolbar.ToolBar;


/**
 * Grid for bean sub-types
 *
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 * @param <M>
 */
public abstract class DkEditorGrid<M extends AbstractDkBeanWithId> extends AbstractDkHideableAdapterField<List<M>> implements HasAddButtonSelectHandlers,
		HasCreateButtonSelectHandlers, HasDeleteButtonSelectHandlers<M>, HasEditButtonSelectHandlers<M>, UIInvalidatable
{

	private final Grid<M> grid;
	private final ListStore<M> store;
	private final IconButtonCell editCell = null;
	private final IconButtonCell deleteCell = null;
	private TextButton buttonAdd = null;
	private TextButton buttonCreate = null;
	private final ToolBar toolbar;
	private final ToolBar bottomToolbar;
	private final boolean topToolbarVisible;
	private boolean headerVisible = false;
	private boolean enabled = true;

	private final VerticalLayoutContainer verticalLayoutContainer;

	/**
	 * Create an Editor grid
	 *
	 * @param listStore
	 *           the store
	 * @param columnConfigs
	 *           the list of column configs
	 * @param props
	 *           the property access
	 * @param functionalities
	 *           any number of functionalities in {@link DkEditorFunctionality}
	 */
	public DkEditorGrid(final ListStore<M> listStore, final List<ColumnConfig<M, ?>> columnConfigs, final DkEditDeletePropertyAccess<M> props,
			final DkEditorFunctionality... functionalities)
	{
		this(listStore, columnConfigs, props, convertArrayToSet(functionalities));
	}

	private static final Set<DkEditorFunctionality> convertArrayToSet(final DkEditorFunctionality[] functionalities)
	{
		final Set<DkEditorFunctionality> setOfFunctionalities = new HashSet<DkEditorFunctionality>();
		for (final DkEditorFunctionality functionality : functionalities)
		{
			if (functionality != null)
			{
				setOfFunctionalities.add(functionality);
			}
		}
		return setOfFunctionalities;
	}

	/**
	 * Create an Editor grid
	 *
	 * @param listStore
	 *           the store
	 * @param columnConfigs
	 *           the list of column configs
	 * @param props
	 *           the property access
	 * @param functionalities
	 *           any number of functionalities in {@link DkEditorFunctionality}
	 */
	public DkEditorGrid(final ListStore<M> listStore, final List<ColumnConfig<M, ?>> columnConfigs, final DkEditDeletePropertyAccess<M> props,
			final Collection<DkEditorFunctionality> functionalities)
	{
		this(listStore, columnConfigs, props, functionalities, false, !functionalities.isEmpty());
	}

	/**
	 * Create an Editor grid
	 *
	 * @param listStore
	 *           the store
	 * @param columnConfigs
	 *           the list of column configs
	 * @param props
	 *           the property access
	 * @param functionalities
	 *           any number of functionalities in {@link DkEditorFunctionality}
	 * @param bottomToolbarVisible
	 *           indicating whether to show or not the bottom toolbar (default is false)
	 * @param topToolbarVisible
	 *           indicating whether to show or not the top toolbar (default is true if at least 1
	 *           {@link DkEditorFunctionality} in ADD and CREATE, else false)
	 */
	public DkEditorGrid(final ListStore<M> listStore, final List<ColumnConfig<M, ?>> columnConfigs, final DkEditDeletePropertyAccess<M> props,
			final Collection<DkEditorFunctionality> functionalities, final boolean bottomToolbarVisible, final boolean topToolbarVisible)
	{
		super(new ContentPanel());
		this.topToolbarVisible = topToolbarVisible;
		verticalLayoutContainer = new VerticalLayoutContainer();
		getContentPanel().add(verticalLayoutContainer);
		getContentPanel().setHeaderVisible(false);
		setHeight(DkBaseMain.config().getEditorGridHeight());
		this.store = listStore;
		final List<ColumnConfig<M, ?>> columns = new ArrayList<ColumnConfig<M, ?>>(columnConfigs);

		if (functionalities.contains(DkEditorFunctionality.EDIT))
		{
			final ColumnConfig<M, String> columnEdit = new DkEditColumnConfig<M>(props.edit(), getEditTooltip(), listStore)
			{
				@Override
				protected boolean isCellIconVisible(final M model, final int column, final int row, final String modelKey)
				{
					return isModelEditable(model, column, row, modelKey);
				}

				@Override
				protected void onClick(final M model, final int column, final int row, final String modelKey)
				{
					if (isEnabled())
					{
						DkEditorGrid.this.fireEvent(new EditButtonSelectEvent<M>(model));
					}
				}
			};
			columns.add(columnEdit);
		}

		if (functionalities.contains(DkEditorFunctionality.DELETE))
		{
			final ColumnConfig<M, String> columnDelete = new DkDeleteColumnConfig<M>(props.delete(), getDeleteTooltip(), listStore)
			{
				@Override
				protected boolean isCellIconVisible(final M model, final int column, final int row, final String modelKey)
				{
					return isModelDeletable(model, column, row, modelKey);
				}

				@Override
				protected void onClick(final M model, final int column, final int row, final String modelKey)
				{
					if (isEnabled())
					{
						DkEditorGrid.this.fireEvent(new DeleteButtonSelectEvent<M>(model));
					}
				}
			};
			columns.add(columnDelete);
		}

		final ColumnModel<M> cm = new ColumnModel<M>(columns);

		grid = new Grid<M>(listStore, cm);
		new QuickTip(grid);
		grid.setBorders(false);
		verticalLayoutContainer.setBorders(false);
		toolbar = new ToolBar();
		toolbar.add(new FillToolItem());
		bottomToolbar = new ToolBar();
		if (functionalities.contains(DkEditorFunctionality.ADD))
		{
			buttonAdd = new TextButton(DkBaseMain.i18n().label_add(), new SelectEvent.SelectHandler()
			{
				@Override
				public void onSelect(final SelectEvent event)
				{
					fireEvent(new AddButtonSelectEvent());
				}
			});
			buttonAdd.setIcon(DkBaseMain.icons().file_plus_16());

			toolbar.add(buttonAdd);
		}
		if (functionalities.contains(DkEditorFunctionality.CREATE))
		{
			buttonCreate = new TextButton(DkBaseMain.i18n().label_create(), new SelectEvent.SelectHandler()
			{
				@Override
				public void onSelect(final SelectEvent event)
				{
					fireEvent(new CreateButtonSelectEvent());
				}
			});
			buttonCreate.setIcon(DkBaseMain.icons().file_plus_16());

			toolbar.add(buttonCreate);
		}
		if (topToolbarVisible)
		{
			toolbar.setHeight(29);
			verticalLayoutContainer.add(toolbar, new VerticalLayoutData(1, 29));
		}
		verticalLayoutContainer.add(grid, new VerticalLayoutData(1, 1));
		if (bottomToolbarVisible)
		{
			bottomToolbar.setHeight(27);
			DkUIUtils.updateBottomToolbarStyle(bottomToolbar);
			verticalLayoutContainer.add(bottomToolbar, new VerticalLayoutData(1, 27));
		}
	}

	protected boolean isModelEditable(final M model, final int column, final int row, final String modelKey)
	{
		return true;
	}

	protected boolean isModelDeletable(final M model, final int column, final int row, final String modelKey)
	{
		return true;
	}

	/**
	 * Editor.forceLayout -> ResizeContainer.forceLayout -> SimpleContainer.forceLayout -> ResizeContainer.applyLayout ->
	 * ContentPanel.setPixelSize<br>
	 *
	 * if(lastSize != new size) ContentPanel.onResize -> Dans cette methode on a getContainerTarget().setHeight(...)<br>
	 * <br>
	 * The ContentPanel onResize method was not called when the ContentPanel becomes visible due to size caching
	 */
	@Override
	public void setVisible(final boolean visible)
	{
		//		if (visible && !isVisible())
		//		{
		//			getContentPanel().clearSizeCache();
		//		}
		super.setVisible(visible);
	}

	/**
	 * @return this editor grid {@link ContentPanel}
	 */
	public ContentPanel getContentPanel()
	{
		return (ContentPanel) getWidget();
	}

	/**
	 * Sets the heading of the {@link ContentPanel}
	 *
	 * @param heading
	 *           the heading to be set
	 */
	public void setHeadingHtml(final String heading)
	{
		getContentPanel().setHeaderVisible(true);
		getContentPanel().setHeadingHtml(heading);
		headerVisible = true;
	}

	/**
	 * Enable or disable buttons
	 *
	 * @param enabled
	 */
	@Override
	public void setEnabled(final boolean enabled)
	{
		if (buttonAdd != null)
		{
			buttonAdd.setEnabled(enabled);
		}
		if (buttonCreate != null)
		{
			buttonCreate.setEnabled(enabled);
		}
		this.enabled = enabled;
		DkUIUtils.setGridContentEnabled(grid, enabled, disabledStyle);
	}

	/**
	 * Is enabled coherent with {@link #setEnabled(boolean)}
	 */
	@Override
	public final boolean isEnabled()
	{
		return enabled;
	}

	/**
	 * @return the edit tooltip, no tooltip (null) by default
	 */
	protected String getEditTooltip()
	{
		return null;
	}

	/**
	 * @return the delete tooltip, no tooltip (null) by default
	 */
	public String getDeleteTooltip()
	{
		return null;
	}

	/**
	 * Set the add button tooltip
	 *
	 * @param tooltip
	 *           the tooltip
	 */
	public void setAddTooltip(final String tooltip)
	{
		if (buttonAdd != null)
		{
			buttonAdd.setTitle(tooltip);
		}
	}

	/**
	 * Set the add button label
	 *
	 * @param label
	 *           the label
	 */
	public void setAddLabel(final String label)
	{
		if (buttonAdd != null)
		{
			buttonAdd.setText(label);
		}
	}

	/**
	 * Set the add button tooltip
	 *
	 * @param tooltip
	 *           the tooltip
	 */
	public void setCreateTooltip(final String tooltip)
	{
		if (buttonCreate != null)
		{
			buttonCreate.setTitle(tooltip);
		}
	}

	/**
	 * Set the add button label
	 *
	 * @param label
	 *           the label
	 */
	public void setCreateLabel(final String label)
	{
		if (buttonCreate != null)
		{
			buttonCreate.setText(label);
		}
	}

	/**
	 * Add a select handler on Add button
	 */
	@Override
	public HandlerRegistration addAddSelectHandler(final AddButtonSelectHandler handler)
	{
		return addHandler(handler, AddButtonSelectEvent.getType());
	}

	/**
	 * Add a select handler on edit button
	 */
	@Override
	public HandlerRegistration addEditSelectHandler(final EditButtonSelectHandler<M> handler)
	{
		return addHandler(handler, EditButtonSelectEvent.getType());
	}

	/**
	 * Add a select handler on delete button
	 */
	@Override
	public HandlerRegistration addDeleteSelectHandler(final DeleteButtonSelectHandler<M> handler)
	{
		return addHandler(handler, DeleteButtonSelectEvent.getType());
	}

	/**
	 * Add a select handler on create button
	 */
	@Override
	public HandlerRegistration addCreateSelectHandler(final CreateButtonSelectHandler handler)
	{
		return addHandler(handler, CreateButtonSelectEvent.getType());
	}

	/**
	 * @return the store
	 */
	public ListStore<M> getStore()
	{
		return store;
	}

	/**
	 * @return the grid
	 */
	public Grid<M> getGrid()
	{
		return grid;
	}

	/**
	 * Get the value
	 */
	@Override
	public List<M> getValue()
	{
		return getStore().getAll();
	}

	/**
	 * Set the value
	 */
	@Override
	public void setValue(final List<M> value)
	{
		getStore().clear();
		getStore().addAll(value);
	}

	/**
	 * @return the toolbar
	 */
	public ToolBar getToolbar()
	{
		return toolbar;
	}

	/**
	 * @return the bottomToolbar
	 */
	public ToolBar getBottomToolbar()
	{
		return bottomToolbar;
	}

	/**
	 * @return the topToolbarVisible
	 */
	public boolean isTopToolbarVisible()
	{
		return topToolbarVisible;
	}

	/**
	 * @return the headerVisible
	 */
	public boolean isHeaderVisible()
	{
		return headerVisible;
	}

	/**
	 * @return the verticalLayoutContainer
	 */
	public VerticalLayoutContainer getVerticalLayoutContainer()
	{
		return verticalLayoutContainer;
	}

	/**
	 * @return the buttonAdd
	 */
	public TextButton getButtonAdd()
	{
		return buttonAdd;
	}

	/**
	 * @return the buttonCreate
	 */
	public TextButton getButtonCreate()
	{
		return buttonCreate;
	}
}
